﻿using System;
using System.Collections.Generic;
using ShadeTree.Binding.Dialogs;

namespace ShadeTree.WinForms.Tests
{
    public class UnexpectedMessageBoxException : ApplicationException
    {
        public UnexpectedMessageBoxException(string caption)
            : base(string.Format("Unexpected MessageBox with caption '{0}'", caption))
        {
        }
    }

    public class MissingMessageBoxException : ApplicationException
    {
        private string _message = "Expected dialogs or prompts were not raised!";

        public override string Message
        {
            get { return _message; }
        }

        public void MissingMessage(UserResponse response)
        {
            _message += string.Format("\nMissing dialog '{0}'", response.Caption);
        }

        public void MissingPrompt(UserResponse response)
        {
            _message += string.Format("\nMissing prompt '{0}'", response.Caption);
        }
    }

    public class MockMessageBoxCreator : IMessageBoxCreator
    {
        private readonly Queue<UserResponse> _expectedMessages
            = new Queue<UserResponse>();

        private readonly Queue<UserResponse> _expectedPrompts
            = new Queue<UserResponse>();

        #region IMessageBoxCreator Members

        public bool Disabled { get; set;}

        public void ShowMessage(string caption, string body)
        {
            if (Disabled) return;

            if (_expectedMessages.Count == 0)
            {
                throw new UnexpectedMessageBoxException(caption);
            }

            UserResponse response = _expectedMessages.Peek();
            if (response.Matches(caption, body))
            {
                _expectedMessages.Dequeue();
            }
            else
            {
                throw new UnexpectedMessageBoxException(caption);
            }
        }

        public void ShowMessage(UserMessage message)
        {
            ShowMessage(message.Caption, message.Body);
        }

        public bool PromptUser(string caption, string body)
        {
            if (Disabled) return true;

            if (_expectedPrompts.Count == 0)
            {
                throw new UnexpectedMessageBoxException(caption);
            }

            UserResponse response = _expectedPrompts.Peek();
            if (response.Matches(caption, body))
            {
                _expectedPrompts.Dequeue();
                return response.Response;
            }
            else
            {
                throw new UnexpectedMessageBoxException(caption);
            }
        }

        public bool PromptUser(UserMessage message)
        {
            return PromptUser(message.Caption, message.Body);
        }

        #endregion

        public void ExpectMessage(string caption, string body)
        {
            var response = new UserResponse(caption, body);
            _expectedMessages.Enqueue(response);
        }

        public void ExpectPrompt(string caption, string body, bool response)
        {
            var userResponse = new UserResponse(caption, body, response);
            _expectedPrompts.Enqueue(userResponse);
        }

        public void Clear()
        {
            _expectedMessages.Clear();
            _expectedPrompts.Clear();
        }

        public void Verify()
        {
            if (Disabled) return;

            if (_expectedPrompts.Count == 0 && _expectedMessages.Count == 0)
            {
                return;
            }

            var exception = new MissingMessageBoxException();
            foreach (UserResponse response in _expectedMessages)
            {
                exception.MissingMessage(response);
            }

            foreach (UserResponse prompt in _expectedPrompts)
            {
                exception.MissingPrompt(prompt);
            }

            Clear();
            throw exception;
        }

        public IDisposable Verification()
        {
            return new VerificationObject(this);
        }

        #region Nested type: VerificationObject

        internal class VerificationObject : IDisposable
        {
            private readonly MockMessageBoxCreator _parent;

            internal VerificationObject(MockMessageBoxCreator parent)
            {
                _parent = parent;
                _parent.Clear();
            }

            #region IDisposable Members

            public void Dispose()
            {
                _parent.Verify();
            }

            #endregion
        }

        #endregion
    }
}